#ifndef LOGGER_H
#define LOGGER_H
#ifdef ignore
#undef ignore
#endif
#include <stdarg.h>
#include <string>
#include <string.h>
#include <fstream>
#include <stdint.h>
#include <pthread.h>
#include "syscore.h"

#define LOG_EMERG             0
#define LOG_ALERT             1
#define LOG_CRIT              2
#define LOG_ERR               3
#define LOG_WARNING           4
#define LOG_NOTICE            5
#define LOG_INFO              6
#define LOG_DEBUG             7
#define LOG_USE_GLOBAL_FILTER 8

#define LoggerPtr Logger::ptr()

class Logger
{
public:
    enum LOG_MODE
    {
        LOG_REAL_TIME = 0,
        LOG_MESSAGE = 1
    };

    typedef uint32_t (*MSGSENDTYPE)(uint16_t, MSGHEADER*, void*);
    
    static Logger *ptr();
    
    bool initialize(std::string comName, std::string path,  LOG_MODE mode, MSGSENDTYPE func=NULL);
    bool unintialize();
    bool SetLogLevel(int32_t log_Level);
    int32_t logMessageFilter();    

    void LogEmergency(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogAlert(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogCritical(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogError(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogWarning(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogNotice(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogInfo(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogDebug(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...);
    void LogByteDump(const char* filename, 
                    uint32_t lineNumber, 
                    const int32_t logLevel,
                    const uint8_t *bytes,
                    const int32_t numberOfBytes);
    void LogByteDump(const char* filename, 
                    uint32_t lineNumber, 
                    const int32_t logLevel,
                    const char *bytes,
                    const int32_t numberOfBytes);
    void lock();
    void unlock();
   
    void ServerLogToStream(std::string logContent);

    LOG_MODE getMode();
    void LogMessage(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char* msgFormat, va_list args);
private:
    Logger();
    Logger(const Logger&);
    Logger& operator=(const Logger&);
    bool RollOverLog();  
    void LogToStdout(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char *const originalLogMessage);
    void LogToStream(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char *const originalLogMessage);
    void LogMessage(const char* filename, uint32_t lineNumber, int16_t moduleId, const int32_t logLevel, const char *const originalLogMessage);

    LOG_MODE mode_;
    static Logger *InstanceM;
    pthread_mutex_t counter_mutex;
    int32_t CurrentLogLine;
    int32_t MaxLogLine;
    uint32_t MessageCouter;
    
    std::string ComponentNameM;
    std::string LogFileNameM;
    bool InitializedM;
    bool ShouldLogToScreen;
    bool ShouldLogToFile;
    bool RollOverLogM;  

    int32_t CountLog[LOG_USE_GLOBAL_FILTER];
    std::ofstream  LogStream;
     
    int32_t LogLevel;
    enum{ LogBufferLengthM = 4096 };
    char OriginalLogMessage[LogBufferLengthM];
    
    uint32_t pidM;

    MSGSENDTYPE msgSendPtr;
};


inline void Logger::lock()
{
    pthread_mutex_lock(&counter_mutex);
}

inline void Logger::unlock()
{
    pthread_mutex_unlock(&counter_mutex);
}

#define LOGGER_LOG_METHOD_BODY(logLevel)\
    va_list ap;\
    va_start(ap, msgFormat);\
    char msgBuf[LogBufferLengthM];\
    if( getMode()==Logger::LOG_REAL_TIME ) {\
    vsnprintf(OriginalLogMessage, LogBufferLengthM, msgFormat, ap);}\
    else{\
    memset(msgBuf, 0, sizeof(msgBuf));\
    vsnprintf(msgBuf, LogBufferLengthM, msgFormat, ap);}\
    va_end(ap);\
    ++CountLog[logLevel];\
    const char* msgPtr = OriginalLogMessage;\
    if( getMode()==Logger::LOG_MESSAGE) {\
    msgPtr = msgBuf;}\
    if (!InitializedM)\
    {\
        LogToStdout(filename, lineNumber, moduleId, logLevel, msgPtr);\
        return;\
    }\
    LogMessage(filename, lineNumber, moduleId, logLevel, msgPtr)
       

inline int Logger::logMessageFilter()
{
    return LogLevel;
}

inline Logger *Logger::ptr()
{
    Logger *Pointer = InstanceM;
    if ( InstanceM == NULL )
    {
        Pointer = InstanceM = new Logger();
    }
    return Pointer;
}
 
inline void Logger::LogEmergency(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{ 
    LOGGER_LOG_METHOD_BODY(LOG_EMERG);
}

inline void Logger::LogAlert(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_ALERT);
}

inline void Logger::LogCritical(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_CRIT);    
}

inline void Logger::LogError(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_ERR);
}

inline void Logger::LogWarning(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{
    LOGGER_LOG_METHOD_BODY(LOG_WARNING);
}

inline void Logger::LogNotice(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{
   LOGGER_LOG_METHOD_BODY(LOG_NOTICE);
}

inline void Logger::LogInfo(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{
   LOGGER_LOG_METHOD_BODY(LOG_INFO);
}

inline void Logger::LogDebug(const char *filename, uint32_t lineNumber, int16_t moduleId, const char* const msgFormat, ...)
{
   LOGGER_LOG_METHOD_BODY(LOG_DEBUG);
}

#define LogEmergency(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_EMERG) LoggerPtr->LogEmergency(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#define LogAlert(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_ALERT) LoggerPtr->LogAlert(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#define LogCritical(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_CRIT) LoggerPtr->LogCritical(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#define LogError(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_ERR) LoggerPtr->LogError(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#define LogWarning(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_WARNING) LoggerPtr->LogWarning(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#define LogNotice(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_NOTICE) LoggerPtr->LogNotice(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#define LogInfo(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_INFO) LoggerPtr->LogInfo(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#define LogDebug(moduleId, fmt, args...) if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->lock(); }\
if (LoggerPtr->logMessageFilter() >= LOG_DEBUG) LoggerPtr->LogDebug(shortname(__FILE__), __LINE__, moduleId, "[0x%04x]:"fmt,  moduleId, ##args);\
if( LoggerPtr->getMode()==Logger::LOG_REAL_TIME ){\
    LoggerPtr->unlock(); }

#ifdef __cplusplus
extern "C" {
#endif

RESULT LogServer_init(const char* comName, const char* path, int mode);

#ifdef __cplusplus
}
#endif

#endif
